﻿using Microsoft.Win32;
using Pdf2epub.BusinessLogic;
using Pdf2epub.Helpers;
using Pdf2epub.Model;
using Pdf2epub.UI;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Ribbon;
using System.Windows.Data;
using System.Windows.Input;
using Xceed.Wpf.AvalonDock.Layout;
using Xceed.Wpf.AvalonDock.Layout.Serialization;

namespace Pdf2epub
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        private const string c_WindowLayoutFile = "layout.xml";
        private readonly Controler m_Controler = new Controler();
        private ListSortDirection m_LastFontAnalysisSortDirection;
        private GridViewColumnHeader m_LastFontAnaysisHeaderClicked;
        private int m_LastFoundFontOccuranceIndex = -1;

        public MainWindow()
        {
            InitializeComponent();

            propertyGrid.SelectedObject = m_Controler.ProjectContext.ProjectConfiguration;

            foreach (HtmlReplacementTag tag in HtmlReplacement.AllParagraphTags)
            {
                MenuItem newItem = new MenuItem
                {
                    Header = tag.ToString(),
                    Command = Pdf2EpubCommands.OverrideHtmlTag,
                    CommandParameter = tag
                };
                cmOverrideHtmlTag.Items.Add(newItem);
            }

            RestoreDataBindings();
        }

        #region Properties

        public TextReplacementCollection CharacterReplacements
        {
            get { return m_Controler.ProjectContext.ProjectConfiguration.TextReplacements; }
        }

        public FontAssignmentDataCollection FontDisplayAssignmentDatas
        {
            get { return m_Controler.ProjectContext.ProjectConfiguration.FontAssignmentDataCollection; }
        }

        public HtmlReplacementCollection PossibleHtmlReplacement
        {
            get { return m_Controler.ProjectContext.ProjectConfiguration.HtmlReplacements; }
        }

        public ContentBlockCollection PreviewSource
        {
            get { return m_Controler.ProjectContext.Content; }
        }

        #endregion Properties

        #region Command workers

        private void AssignReplacement(FontAssignmentData fontAssignmentData, HtmlReplacement htmlReplacement)
        {
            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Updating...");
                m_Controler.ProjectContext.FeedbackService.SetDetailText("Please wait...");

                if (fontAssignmentData == null)
                    return;

                m_Controler.AssignFont(fontAssignmentData, htmlReplacement);
            }
        }

        private void AssignReplacement(IEnumerable<ContentBlock> contentBlocks, HtmlReplacement htmlReplacement)
        {
            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Updating...");
                m_Controler.ProjectContext.FeedbackService.SetDetailText("Please wait...");

                foreach (ContentBlock block in contentBlocks)
                {
                    m_Controler.AssignFont(block, htmlReplacement);
                }
            }
        }

        private void DeleteSelectedContentBlock()
        {
            if (lvContentBlocks.SelectedIndex < 0)
                return;

            m_Controler.DeleteContentBlockAtIndex(lvContentBlocks.SelectedIndex);
        }

        private async void ExtractSource()
        {
            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Extracting source document");

                await m_Controler.ExtractFromSourceAsync();
            }
        }

        private void FindNextMatchingContentBlock(Func<ContentBlock, bool> criteriaMatch)
        {
            if (PreviewSource.Count == 0)
            {
                MessageBox.Show("Preview is empty. Render preview first!");
                return;
            }

            if (lvContentBlocks.SelectedIndex >= 0)
                m_LastFoundFontOccuranceIndex = lvContentBlocks.SelectedIndex;

            int lastFoundBackup = m_LastFoundFontOccuranceIndex;
            m_LastFoundFontOccuranceIndex++;

            if (lastFoundBackup < 0)
                lastFoundBackup = 0;

            if (lastFoundBackup > PreviewSource.Count)
                lastFoundBackup = 0;

            while (m_LastFoundFontOccuranceIndex != lastFoundBackup)
            {
                if (m_LastFoundFontOccuranceIndex > (PreviewSource.Count - 1))
                    m_LastFoundFontOccuranceIndex = 0;

                if (m_LastFoundFontOccuranceIndex == lastFoundBackup)
                    break;

                ContentBlock contentBlock = PreviewSource[m_LastFoundFontOccuranceIndex];
                if ((contentBlock != null) && criteriaMatch(contentBlock))
                {
                    lvContentBlocks.SelectedValue = contentBlock;
                    lvContentBlocks.ScrollIntoView(contentBlock);
                    break;
                }

                m_LastFoundFontOccuranceIndex++;
            }
        }

        private void FindNextOccuranceOfFontInHtml(FontAssignmentData fontAssignment)
        {
            FindNextMatchingContentBlock(contentBlock => (contentBlock.Font != null) && (contentBlock.Font.ToString() == fontAssignment.FontName));
        }

        private void FindText()
        {
            string searchText = searchTextBox.Text.ToLowerInvariant();
            if (string.IsNullOrEmpty(searchText))
                return;

            FindNextMatchingContentBlock(contentBlock =>
                (!string.IsNullOrEmpty(contentBlock.Content)) && (contentBlock.Content.ToLowerInvariant().Contains(searchText)));
        }

        private void GotoPage()
        {
            string input = gotoPageTextBox.Text;

            int page;
            if (!int.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out page))
                return;

            ContentBlock contentBlock = m_Controler.ProjectContext.Content.FirstOrDefault(block => block.SourcePage == page);
            if (contentBlock == null)
                return;

            lvContentBlocks.SelectedValue = contentBlock;
            lvContentBlocks.ScrollIntoView(contentBlock);
        }

        private void InsertNewTableBlockAfterSelectedBlock()
        {
            if (lvContentBlocks.SelectedIndex < 0)
                return;

            m_Controler.InsertEmptyTableBlockAtIndex(lvContentBlocks.SelectedIndex);
        }

        private void InsertNewTextBlockAfterSelectedBlock()
        {
            if (lvContentBlocks.SelectedIndex < 0)
                return;

            m_Controler.InsertEmptyTextBlockAtIndex(lvContentBlocks.SelectedIndex);
        }

        private void JoinBlocks(IEnumerable<ContentBlock> contentBlocks)
        {
            foreach (ContentBlock block in contentBlocks)
            {
                m_Controler.JoinBlocks(block);
            }
        }

        private async void OpenPdfFile()
        {
            if (!string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath))
            {
                SaveAfterUserRequestIfRequired();
            }

            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Opening document");

                OpenFileDialog dlg = new OpenFileDialog
                                         {
                                             Filter =
                                                 "All supported documents|*.pdf;*.zip|PDF documents|*.pdf|Galileo Open Book Archive|*.zip",
                                             FilterIndex = 0,
                                             Multiselect = false
                                         };

                if (dlg.ShowDialog(this) == true)
                {
                    try
                    {
                        string filename = Path.GetFileName(dlg.FileName);
                        m_Controler.ProjectContext.FeedbackService.SetDetailText(filename);

                        await m_Controler.OpenPdfAsync(dlg.FileName);

                        RestoreDataBindings();

                        pgContextBlock.SelectedObject = null;
                        selectedContentBlockPreview.Navigate("about:blank");
                    }
                    catch (InvalidFileFormatException ifex)
                    {
                        MessageBox.Show(ifex.Message);
                    }
                }
            }
        }

        private void OpenTableConfiguration()
        {
            TableDetectionConfigurator dlg = new TableDetectionConfigurator(m_Controler.ProjectContext);
            dlg.Owner = this;
            dlg.ShowDialog();
        }

        private void OpenStyleEditor()
        {
            StyleEditor styleEditor = new StyleEditor();
            styleEditor.ProjectContext = m_Controler.ProjectContext;
            styleEditor.ShowDialog();
        }

        private void OverrideHtmlTag(IEnumerable<ContentBlock> contentBlocks, HtmlReplacementTag tag)
        {
            foreach (ContentBlock block in contentBlocks)
            {
                m_Controler.OverrideHtmlTag(block, tag);
            }
        }

        private void OverrideReplacement(IEnumerable<ContentBlock> contentBlocks, HtmlReplacement htmlReplacement)
        {
            foreach (ContentBlock contentBlock in contentBlocks)
            {
                m_Controler.OverrideAssignment(contentBlock, htmlReplacement);
            }
        }

        private string PatchImagePathsForPreview(string html)
        {
            string newImageParentPath = m_Controler.ProjectContext.ProjectConfiguration.HtmlOutputPath;
            const string stringToReplace = "src=\"Images";
            string replacementString = string.Format("src=\"{0}\\Images", newImageParentPath);

            return html.Replace(stringToReplace, replacementString);
        }

        private void RestoreDataBindings()
        {
            m_Controler.ProjectContext.FeedbackService = new FeedbackService(busyIndicator);

            propertyGrid.SelectedObject = m_Controler.ProjectContext.ProjectConfiguration;
            dgReplacementCharacters.ItemsSource = CharacterReplacements;
            FontOverview.ItemsSource = FontDisplayAssignmentDatas;
            lvContentBlocks.ItemsSource = m_Controler.ProjectContext.Content;
        }

        private async void RunFontAnalysis()
        {
            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Running Font Analysis");

                await m_Controler.RunFontAnalysisAsync();

                anchorableFontAnalysis.IsSelected = true;
            }
        }

        private void Save()
        {
            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Saving document metadata");
                m_Controler.Save();
            }
        }

        private void SetChapterTitle()
        {
            throw new NotImplementedException();
        }

        private async void TransformHtmlToEpub()
        {
            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Transforming HTML to EPub");
                await m_Controler.TransformHtmlToEpubAsync();
            }
        }

        private async void TransformToHtml()
        {
            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Transforming to HTML...");

                string pdfDirname = Path.GetDirectoryName(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
                if (string.IsNullOrEmpty(pdfDirname))
                    return;

                string targetFolder = Path.Combine(pdfDirname, m_Controler.HtmlFolder);
                if (Directory.Exists(targetFolder))
                {
                    if (MessageBox.Show(string.Format("Output folder '{0}' exists. Overwrite?", targetFolder),
                                        "HTML Output",
                                        MessageBoxButton.YesNo, MessageBoxImage.Warning) == MessageBoxResult.Yes)
                    {
                        try
                        {
                            foreach (string file in Directory.GetFiles(targetFolder, "*.html"))
                            {
                                File.Delete(file);
                            }
                        }
                        catch (IOException ex)
                        {
                            MessageBox.Show(string.Format("Error deleting existing output files: '{0}'", ex), "Error",
                                            MessageBoxButton.OK, MessageBoxImage.Error);
                        }
                    }
                    else
                    {
                        return;
                    }
                }

                await m_Controler.TransformToHtmlAsync(targetFolder);
            }
        }

        private void UnJoinBlocks(IEnumerable<ContentBlock> contentBlocks)
        {
            foreach (ContentBlock block in contentBlocks)
            {
                m_Controler.UnJoinBlocks(block);
            }
        }

        #endregion Command workers

        #region Command handling

        private static ListView GetListViewFromListViewItem(ListViewItem listViewItem)
        {
            return ItemsControl.ItemsControlFromItemContainer(listViewItem) as ListView;
        }

        private static IEnumerable<ContentBlock> GetSelectedContentBlocks(ListView listView)
        {
            return listView.SelectedItems.Cast<ContentBlock>().ToList();
        }

        private void AnalyzeFontCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
        }

        private void AssignReplacemenCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
            if (listViewItem != null)
            {
                if ((listViewItem.Content is ContentBlock) || (listViewItem.Content is FontAssignmentData))
                {
                    e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
                }
            }
        }

        private void BulkJobOnBlocksCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = m_Controler.ProjectContext.Content.Any();
        }

        private void ContentBlockOperationCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = m_Controler.ProjectContext.Content.Any();
        }

        private void OpenTableConfigurationCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            bool canExecute = false;
            if (!string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath))
            {
                string extension = Path.GetExtension(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
                if (extension != null)
                {
                    canExecute = extension.Equals(".pdf", StringComparison.InvariantCultureIgnoreCase);
                }
            }
            e.CanExecute = canExecute && m_Controler.ProjectContext.ProjectConfiguration.DetectTables;
        }

        private void EditStylesCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !(string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath));
        }

        private void ExtractSourceCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
        }

        private void FindTextCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath) && (m_Controler.ProjectContext.Content.Count > 0);
        }

        private void GotoPageCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath) && (m_Controler.ProjectContext.Content.Count > 0);
        }

        private void JoinBlockCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = m_Controler.ProjectContext.Content.Any();
        }

        private void JoinMarkedBlocksCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = m_Controler.ProjectContext.Content.Any(block => block.JoinWithPreviousBlock);
        }

        private void OnCommandExecuted(object sender, ExecutedRoutedEventArgs e)
        {
            if (e.Command is RoutedUICommand)
            {
                RoutedUICommand uiCommand = e.Command as RoutedUICommand;
                switch (uiCommand.Name)
                {
                    case "FindText":
                        FindText();
                        break;

                    case "GotoPage":
                        GotoPage();
                        break;

                    case "OpenPdf":
                        OpenPdfFile();
                        break;

                    case "Quit":
                        Close();
                        break;

                    case "ExtractSource":
                        ExtractSource();
                        break;

                    case "AnalyzeFonts":
                        RunFontAnalysis();
                        break;

                    case "EditStyles":
                        OpenStyleEditor();
                        break;

                    case "OpenTableConfiguration":
                        OpenTableConfiguration();
                        break;

                    case "TransformHtml":
                        TransformToHtml();
                        break;

                    case "AssignReplacement":
                        {
                            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
                            if (listViewItem != null)
                            {
                                ListView listView = ItemsControl.ItemsControlFromItemContainer(listViewItem) as ListView;

                                if (Equals(listView, lvContentBlocks))
                                {
                                    IEnumerable<ContentBlock> selectedContentBlocks = GetSelectedContentBlocks(listView);
                                    AssignReplacement(selectedContentBlocks, (HtmlReplacement)e.Parameter);
                                }
                                else if (listViewItem.Content is FontAssignmentData)
                                {
                                    AssignReplacement(listViewItem.Content as FontAssignmentData, (HtmlReplacement)e.Parameter);
                                }
                            }
                            break;
                        }
                    case "OverrideReplacement":
                        {
                            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
                            if (listViewItem != null)
                            {
                                ListView listView = GetListViewFromListViewItem(listViewItem);

                                IEnumerable<ContentBlock> selectedContentBlocks = GetSelectedContentBlocks(listView);
                                OverrideReplacement(selectedContentBlocks, (HtmlReplacement)e.Parameter);
                            }
                            break;
                        }
                    case "OverrideHtmlTag":
                        {
                            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
                            if (listViewItem != null)
                            {
                                ListView listView = GetListViewFromListViewItem(listViewItem);

                                IEnumerable<ContentBlock> selectedContentBlocks = GetSelectedContentBlocks(listView);
                                OverrideHtmlTag(selectedContentBlocks, (HtmlReplacementTag)e.Parameter);
                            }
                            break;
                        }
                    case "JoinPreviousBlock":
                        {
                            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
                            if (listViewItem != null)
                            {
                                ListView listView = GetListViewFromListViewItem(listViewItem);

                                IEnumerable<ContentBlock> selectedContentBlocks = GetSelectedContentBlocks(listView);
                                JoinBlocks(selectedContentBlocks);
                            }
                            break;
                        }
                    case "UnJoinPreviousBlock":
                        {
                            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
                            if (listViewItem != null)
                            {
                                ListView listView = GetListViewFromListViewItem(listViewItem);

                                IEnumerable<ContentBlock> selectedContentBlocks = GetSelectedContentBlocks(listView);
                                UnJoinBlocks(selectedContentBlocks);
                            }
                            break;
                        }
                    case "TransformEpub":
                        TransformHtmlToEpub();
                        break;

                    case "SetChapter":
                        SetChapterTitle();
                        break;

                    case "Save":
                        Save();
                        break;

                    case "InsertNewTextblockHere":
                        InsertNewTextBlockAfterSelectedBlock();
                        break;

                    case "InsertNewTableblockHere":
                        InsertNewTableBlockAfterSelectedBlock();
                        break;

                    case "DeleteThisContentBlock":
                        DeleteSelectedContentBlock();
                        break;

                    case "RemoveMarkedBlocks":
                        m_Controler.RemoveMarkedBlocks();
                        break;

                    case "JoinMarkedBlocks":
                        m_Controler.JoinMarkedBlocks();
                        break;

                    case "RemoveDuplicateWhitespaceFromParagraphBlocks":
                        m_Controler.RemoveDuplicateWhitespaceFromParagraphBlocks();
                        break;

                    case "DeleteEmptyContentBlock":
                        m_Controler.RemoveEmptyContentBlocks();
                        break;
                }
            }
        }

        private void OpenPdfCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void OverrideHtmlTagCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
            if (listViewItem != null)
            {
                if ((listViewItem.Content is ContentBlock) || (listViewItem.Content is FontAssignmentData))
                {
                    e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
                }
            }
        }

        private void OverrideStyleCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            ListViewItem listViewItem = e.OriginalSource as ListViewItem;
            if (listViewItem != null)
            {
                if ((listViewItem.Content is ContentBlock) || (listViewItem.Content is FontAssignmentData))
                {
                    e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
                }
            }
        }

        private void RemoveMarkedBlocksCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = m_Controler.ProjectContext.Content.Any();
        }

        private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
        }

        private void SetChapterCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            ListViewItem item = e.OriginalSource as ListViewItem;
            if (item != null)
            {
                ContentBlock contentBlock = item.Content as ContentBlock;
                if (contentBlock != null)
                {
                    e.CanExecute = (contentBlock.Chapter == null);
                    return;
                }
            }
            e.CanExecute = false;
        }

        private void TransformEpubCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            bool projectOpen = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath);
            bool htmlExisits = false;
            if (!string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath))
            {
                if (!string.IsNullOrEmpty(Path.GetDirectoryName(m_Controler.ProjectContext.ProjectConfiguration.PdfPath)))
                {
                    htmlExisits = Directory.Exists(m_Controler.HtmlFolder);
                }
            }

            e.CanExecute = projectOpen && htmlExisits;
        }

        private void TransformHtmlCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = !string.IsNullOrEmpty(m_Controler.ProjectContext.ProjectConfiguration.PdfPath) && (m_Controler.ProjectContext.Content.Count > 0);
        }

        #endregion Command handling

        #region Window handling

        private static MenuItem CreateHtmlReplacementMenuItem(HtmlReplacement replacement, ICommand command)
        {
            MenuItem newItem = new MenuItem
            {
                Header = replacement.ToString(),
                Command = command,
                CommandParameter = replacement
            };
            return newItem;
        }

        private static int GetColumnIndex(ListView senderListView, GridViewColumnHeader headerClicked)
        {
            GridView viewBase = senderListView.View as GridView;
            if (viewBase != null)
            {
                int columnIndex = viewBase.Columns.IndexOf(headerClicked.Column);
                return columnIndex;
            }
            return 0;
        }

        private static string GetPropertyDisplayName(object descriptor)
        {
            PropertyDescriptor pd = descriptor as PropertyDescriptor;
            if (pd != null)
            {
                DisplayNameAttribute displayName = pd.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
                if (displayName != null && !displayName.Equals(DisplayNameAttribute.Default))
                {
                    return displayName.DisplayName;
                }
            }
            else
            {
                PropertyInfo pi = descriptor as PropertyInfo;
                if (pi != null)
                {
                    Object[] attributes = pi.GetCustomAttributes(typeof(DisplayNameAttribute), true);
                    for (int i = 0; i < attributes.Length; ++i)
                    {
                        DisplayNameAttribute displayName = attributes[i] as DisplayNameAttribute;
                        if (displayName != null && !displayName.Equals(DisplayNameAttribute.Default))
                        {
                            return displayName.DisplayName;
                        }
                    }
                }
            }
            return null;
        }

        private void GeneratingColumnHeadersForDataGrid(object sender, DataGridAutoGeneratingColumnEventArgs e)
        {
            string displayName = GetPropertyDisplayName(e.PropertyDescriptor);
            if (!string.IsNullOrEmpty(displayName))
            {
                e.Column.Header = displayName;
            }
        }

        private IEnumerable<ContentBlock> GetSelectedContentBlocksFromPreviewSource()
        {
            return lvContentBlocks.SelectedItems.Cast<ContentBlock>().ToArray();
        }

        private ListSortDirection GetSortingDirection(GridViewColumnHeader headerClicked)
        {
            ListSortDirection direction;
            if (!headerClicked.Equals(m_LastFontAnaysisHeaderClicked))
            {
                direction = ListSortDirection.Ascending;
            }
            else
            {
                direction = (m_LastFontAnalysisSortDirection == ListSortDirection.Ascending)
                                ? ListSortDirection.Descending
                                : ListSortDirection.Ascending;
            }
            return direction;
        }

        private void OnContextMenuFontOverviewOpening(object sender, ContextMenuEventArgs e)
        {
            cmFontOverviewAssignment.Items.Clear();
            foreach (HtmlReplacement replacement in m_Controler.ProjectContext.ProjectConfiguration.HtmlReplacements)
            {
                cmFontOverviewAssignment.Items.Add(CreateHtmlReplacementMenuItem(replacement, Pdf2EpubCommands.AssignReplacement));
            }
        }

        private void OnContextMenuPreviewSourceOpening(object sender, ContextMenuEventArgs e)
        {
            cmItemPreviewSourceAssignment.Items.Clear();
            foreach (HtmlReplacement replacement in m_Controler.ProjectContext.ProjectConfiguration.HtmlReplacements)
            {
                cmItemPreviewSourceAssignment.Items.Add(CreateHtmlReplacementMenuItem(replacement, Pdf2EpubCommands.AssignReplacement));
            }

            cmOverrideAssignment.Items.Clear();
            foreach (HtmlReplacement replacement in m_Controler.ProjectContext.ProjectConfiguration.HtmlReplacements)
            {
                cmOverrideAssignment.Items.Add(CreateHtmlReplacementMenuItem(replacement, Pdf2EpubCommands.OverrideStyle));
            }
        }

        private void OnFontOverviewColumnHeaderClicked(object sender, RoutedEventArgs e)
        {
            GridViewColumnHeader headerClicked = e.OriginalSource as GridViewColumnHeader;

            ListView senderListView = sender as ListView;

            if ((headerClicked != null) && (senderListView != null))
            {
                if (headerClicked.Role != GridViewColumnHeaderRole.Padding)
                {
                    ListSortDirection direction = GetSortingDirection(headerClicked);

                    int columnIndex = GetColumnIndex(senderListView, headerClicked);

                    Sort(columnIndex, direction, senderListView.ItemsSource);

                    SetSortingArrow(direction, headerClicked);

                    m_LastFontAnaysisHeaderClicked = headerClicked;
                    m_LastFontAnalysisSortDirection = direction;
                }
            }
        }

        private void OnFontOverviewDoubleClick(object sender, MouseButtonEventArgs e)
        {
            FontAssignmentData doubleClickedFontAssignment = ((FrameworkElement)e.OriginalSource).DataContext as FontAssignmentData;
            if (doubleClickedFontAssignment == null)
                return;

            FindNextOccuranceOfFontInHtml(doubleClickedFontAssignment);
        }

        private void OnMainWindowClosing(object sender, CancelEventArgs e)
        {
            SaveAfterUserRequestIfRequired();

            if (MessageBox.Show("Really close?", "Closing...", MessageBoxButton.YesNo) == MessageBoxResult.No)
            {
                e.Cancel = true;
                return;
            }

            SaveWindowLayout();

            using (m_Controler.ProjectContext.FeedbackService.Capsule())
            {
                m_Controler.ProjectContext.FeedbackService.SetBusyText("Saving content to cache file");
            }
        }

        private void OnNewTextReplacementAdded(object sender, AddingNewItemEventArgs e)
        {
            TextReplacement newReplacement = new TextReplacement();
            newReplacement.SetProjectContext(m_Controler.ProjectContext);
            e.NewItem = newReplacement;
        }

        private void OnPreviewSourceListSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            if (e.AddedItems.Count == 0)
                return;

            IEnumerable<ContentBlock> selectedBlocks = GetSelectedContentBlocksFromPreviewSource();

            ContentBlock selectedBlock = e.AddedItems[0] as ContentBlock;
            if (selectedBlock == null)
                return;

            pgContextBlock.SelectedObject = selectedBlock;

            RenderHtmlPreviewForSelection(selectedBlocks);
        }

        private async void RenderHtmlPreviewForSelection(IEnumerable<ContentBlock> selectedBlocks)
        {
            string html = await m_Controler.GetHtmlAsync(selectedBlocks.OrderBy(block => block.SequenceNumber).ToArray());

            html = PatchImagePathsForPreview(html);

            if (!string.IsNullOrEmpty(html))
            {
                selectedContentBlockPreview.NavigateToString(html);
            }
        }

        private void SaveAfterUserRequestIfRequired()
        {
            if (!m_Controler.ProjectContext.IsUnsaved)
                return;
            if (MessageBox.Show("Project contains unsaved data. Do you want to save now?", "Warning", MessageBoxButton.YesNo) == MessageBoxResult.No)
                return;

            Save();
        }

        private void SetSortingArrow(ListSortDirection direction, GridViewColumnHeader headerClicked)
        {
            if (direction == ListSortDirection.Ascending)
            {
                headerClicked.Column.HeaderTemplate = Resources["HeaderTemplateArrowUp"] as DataTemplate;
            }
            else
            {
                headerClicked.Column.HeaderTemplate = Resources["HeaderTemplateArrowDown"] as DataTemplate;
            }

            // Remove arrow from previously sorted header
            if (m_LastFontAnaysisHeaderClicked != null && !m_LastFontAnaysisHeaderClicked.Equals(headerClicked))
            {
                m_LastFontAnaysisHeaderClicked.Column.HeaderTemplate = null;
            }
        }

        private void Sort(int columnNumber, ListSortDirection direction, object itemSource)
        {
            ICollectionView dataView = CollectionViewSource.GetDefaultView(itemSource);

            ListCollectionView listCollectionView = dataView as ListCollectionView;
            if (listCollectionView != null)
            {
                if (listCollectionView.ItemProperties != null)
                {
                    string propertyName = listCollectionView.ItemProperties[columnNumber].Name;

                    dataView.SortDescriptions.Clear();
                    SortDescription sd = new SortDescription(propertyName, direction);
                    dataView.SortDescriptions.Add(sd);
                }
            }
            dataView.Refresh();
        }

        #endregion Window handling

        #region Docking stuff

        private string LayoutFile
        {
            get
            {
                //string exeDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                //return Path.Combine(exeDirectory, c_WindowLayoutFile);
                return c_WindowLayoutFile;
            }
        }

        private void LinkHidingEventOfAnchorables(IEnumerable<ILayoutElement> children)
        {
            foreach (ILayoutElement child in children)
            {
                LayoutAnchorable anchorable = child as LayoutAnchorable;
                LayoutPanel layoutPanel = child as LayoutPanel;
                LayoutAnchorablePaneGroup layoutAnchorablePaneGroup = child as LayoutAnchorablePaneGroup;
                LayoutAnchorablePane layoutAnchorablePane = child as LayoutAnchorablePane;

                if (anchorable != null)
                {
                    anchorable.Hiding += OnHiding;
                    if (anchorable.IsHidden)
                    {
                        SetMenuItemRestoreWindow(anchorable);
                    }
                }

                if (layoutPanel != null)
                {
                    LinkHidingEventOfAnchorables(layoutPanel.Children);
                }
                else if (layoutAnchorablePaneGroup != null)
                {
                    LinkHidingEventOfAnchorables(layoutAnchorablePaneGroup.Children);
                }
                else if (layoutAnchorablePane != null)
                {
                    LinkHidingEventOfAnchorables(layoutAnchorablePane.Children);
                }
            }
        }

        private void OnHiding(object sender, CancelEventArgs e)
        {
            LayoutContent closedElement = sender as LayoutContent;
            if (closedElement == null)
                return;

            SetMenuItemRestoreWindow(closedElement);
        }

        private void OnMainWindowLoaded(object sender, RoutedEventArgs e)
        {
            if (!File.Exists(LayoutFile))
                return;

            var serializer = new XmlLayoutSerializer(dockingManager);
            using (var stream = new StreamReader(LayoutFile))
            {
                serializer.LayoutSerializationCallback += (s, args) =>
                {
                    args.Content = Application.Current.MainWindow.FindName(args.Model.ContentId);
                };
                serializer.Deserialize(stream);
            }

            RestoreMenusForAllHiddenItems();
        }

        private void OnWindowRestoreClicked(object sender, RoutedEventArgs e)
        {
            RibbonButton senderMenu = sender as RibbonButton;
            if (senderMenu == null)
                return;

            LayoutAnchorable closedElement = senderMenu.Tag as LayoutAnchorable;
            if (closedElement == null)
            {
                return;
            }

            closedElement.Show();

            RibbonGroup dependencyObject = senderMenu.Parent as RibbonGroup;
            if (dependencyObject != null)
            {
                dependencyObject.Items.Remove(senderMenu);
            }
        }

        private void RestoreMenusForAllHiddenItems()
        {
            rgClosedWindows.Items.Clear();

            LinkHidingEventOfAnchorables(dockingManager.Layout.Children);
        }

        private void SaveWindowLayout()
        {
            XmlLayoutSerializer serializer = new XmlLayoutSerializer(dockingManager);
            using (var stream = new StreamWriter(LayoutFile))
            {
                serializer.Serialize(stream);
            }
        }

        private void SetMenuItemRestoreWindow(LayoutContent closedElement)
        {
            RibbonButton newItem = new RibbonButton
            {
                LargeImageSource = BitmapToImageSourceConverter.Convert(Properties.Resources.window_new_8),
                Label = closedElement.Title,
                Tag = closedElement,
                CommandParameter = closedElement
            };
            newItem.Click += OnWindowRestoreClicked;

            rgClosedWindows.Items.Add(newItem);
        }

        #endregion Docking stuff
    }
}